前言
Unsafe 类一直是个很神秘的角色,我们普通开发者几乎不会碰到,顶多也是使用了并发包之类的系统类库,间接使用到了而已。那它到底是用来做什么的呢?它提供了我们直接操作内存的接口。Java 本身作为一个内存自动管理的工具,内存的开辟释放由虚拟机代为管理,然而,HotSpot 的设计者留下了 Unsafe 的类,用于扩展,它可以直接开辟内存,释放内存,读取任意地址的内存,而不受 Java 堆内存的限制。尽管如此,它并不能为我们所用,加载这个类只能由系统的类加载器执行,但我们可以通过反射获取到它的实例对象,Java 反射的确很牛啊。
如何获取实例对象
第一个问题:为什么我们需要通过 Field 获取,不能使用 newInstance 获取呢?
通过 newInstance 的方法获取实力需要构造函数是 public 的,否则会抛异常,及时 getUnsafe 是静态函数,我们也不能通过这个去获取,因为这时候类加载不是系统的,会抛异常
1 2 3 4 5 6 7 8 9 10 11 12
| private Unsafe() { }
@CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
|
1
| Unsafe unsafe = Unsafe.class.newInstance();
|
由于 Unsafe 是单例,当类加载时,theUnsafe 实例会被加载,这样我们就可以通过反射获取这个实例
1 2 3 4 5 6
| static { registerNatives(); Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"}); // 重点 theUnsafe = new Unsafe(); }
|
通过 Field 获取
1 2 3 4 5
| public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); }
|
获取到这实例,就可以干很多事情了…
先来看看 API 图



太多了,眼睛都看瞎了
归个类吧
- 直接操作内存,将某个对象的值更改,读取,比如将 String 的 value 更改,是不是很神奇,这个是用反射也可以更改哦!下次面试的时候可以考考别人 String 的 value 怎么才能改的掉,看他会几种。
1 2 3 4 5 6 7 8 9 10 11 12
| String str = "Hello Unsafe"; Field value = str.getClass().getDeclaredField("value"); unsafe.putObject(str, unsafe.objectFieldOffset(value), new char[]{'M', 'a', 'j', 'i', 'c'});] System.out.println(str); // output: Majic
// throw exception String str = "Hello Unsafe"; Field value = str.getClass().getDeclaredField("value");
value.setAccessible(true); value.set(str, new char[] {'f', 'i', 'n', 'a', 'l'});
|
- CompareAndSwap 著名的 CAS
为了保证并发安全, CAS 涉及到的变量应该使用 volatile 修饰,保证读到的值最新
allocateInstance 新建一个没有初始化的实例,各个值都是默认值
compareAndSwapLong 第一个参数是 object,第二个参数是变量内存偏移值,可以用 unsafe 类获取实际偏移值,第三个参数是 期望值,第四个三叔目标值,大致的语义就是:如果内存中是期望值,我就更新为目标值,否则不更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); InnerClass o = (InnerClass)unsafe.allocateInstance(InnerClass.class); o.print(); // print 100 Field a = o.getClass().getDeclaredField("value"); unsafe.putLong(o, unsafe.objectFieldOffset(a), 10000); o.print(); // print 10000 unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 10000, 1111); o.print(); // print 1111 unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 1000, 10000); o.print(); // print 1111 } static class InnerClass { // 保证内存可见性 private volatile long value; InnerClass() { value = 100L; } void print() { System.err.println("value==>" + value); } }
|
- 线程挂起,取消
使用 unsafe.unpark 可以取消 Thread.sleep() 后者 park 的线程
使用 unsafe.park(false, 1000000000) 可以挂起当前线程,第一个参数表示是isAbsolute,是否是绝对时间,后一个参数为时间,flase 表示相对时间,0表示一直挂起,单位为纳秒;true 表示绝对时间,单位为毫秒,System.currentThreadMillis 搭配使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, InterruptedException { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null);
InnerThread innerThread = new InnerThread(unsafe); innerThread.run();
Thread.sleep(20); // 取消挂起 unsafe.unpark(innerThread); }
static class InnerThread extends Thread { Unsafe unsafe;
InnerThread(Unsafe unsafe) { this.unsafe = unsafe; }
@Override public void run() { System.out.println("start"); try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } //unsafe.park(false, 1000000000L); System.out.println("end"); } }
|
小结
以后又可以吹一波了,一箭双雕,反射,Unsafe